package transaction

import (
	"reflect"
	"runtime"
	"sanguo/node/node_game"
	"time"

	"github.com/golang/protobuf/proto"
	"github.com/sniperHW/kendynet"
	"github.com/sniperHW/kendynet/event"
)

const (
	TransTimeoutSec = 10
	TransTimeoutMs  = 10 * 1000
)

var TransTime int64

type TransactionBase struct {
	Expired time.Time
}

type Transaction interface {
	Begin()
	End()
	Timeout()
	GetExpiredTime() time.Time
	SetExpiredTime(time.Time)
	GetReplyMsg() proto.Message
	GetModuleName() string
}

type linkNode struct {
	trans Transaction
	next  *linkNode
}

type TransactionMgr struct {
	current      Transaction //当前正在处理的Transaction
	head         *linkNode
	tail         *linkNode
	size         int
	queue        *event.EventQueue
	onCurrentEnd func()
	inBegin      bool
}

func New(queue *event.EventQueue) *TransactionMgr {
	return &TransactionMgr{
		queue: queue,
	}
}

func pcall(f func()) (ok bool) {

	defer func() {
		if r := recover(); r != nil {
			buf := make([]byte, 65535)
			l := runtime.Stack(buf, false)
			kendynet.Errorf("%v: %s\n", r, buf[:l])
			ok = false
			return
		}
		ok = true
	}()

	f()
	return
}

func (this *TransactionMgr) callBegin(trans Transaction) {

	//moniter
	//go moniter.ReportInfo("SC", reflect.TypeOf(trans).String(), "req")
	//node_game.Debugln("Transaction Begin:", reflect.TypeOf(trans).String())
	if TransTime == 0 {
		TransTime = time.Now().UnixNano() / 1e6
	}

	this.inBegin = true
	ok := pcall(trans.Begin)
	this.inBegin = false
	if !ok {
		this.End(trans)
	}
}

func (this *TransactionMgr) PushTrans(trans Transaction, timeout time.Duration) {
	/*
	* 如果当前没有Transaction正在执行立即开始执行，否则将trans添加到待处理队列中
	 */
	if nil == this.current {
		trans.SetExpiredTime(time.Now().Add(timeout))
		this.current = trans
		this.callBegin(trans)
	} else {
		node := &linkNode{
			trans: trans,
		}
		if nil == this.tail {
			this.head = node
			this.tail = node
		} else {
			this.tail.next = node
			this.tail = node
		}
		this.size++
	}
}

func (this *TransactionMgr) OnCurrentEnd(callback func()) {
	this.onCurrentEnd = callback
}

func (this *TransactionMgr) Clear() {
	this.size = 0
	this.head = nil
	this.tail = nil
}

func (this *TransactionMgr) Tick(now time.Time) {
	if this.current != nil && now.After(this.current.GetExpiredTime()) {
		pcall(this.current.Timeout)
	}
}

func (this *TransactionMgr) CheckTrans(trans Transaction) bool {
	return this.current == trans
}

func (this *TransactionMgr) End(trans Transaction) {
	//moniter
	//go moniter.ReportInfo("SC", reflect.TypeOf(trans).String(), "resp")
	//node_game.Debugln("Transaction End:", reflect.TypeOf(trans).String())
	if TransTime != 0 {
		useTime := time.Now().UnixNano()/1e6 - TransTime
		if useTime > 100 {
			node_game.Debugln("Transaction Time ---------->", reflect.TypeOf(trans).String(), useTime)
		}
		TransTime = 0
	}

	if trans == this.current {
		pcall(this.current.End)
		if nil != this.onCurrentEnd {
			pcall(this.onCurrentEnd)
		}
		this.current = nil
		if this.size > 0 {
			this.size--
			this.current = this.head.trans
			this.head = this.head.next
			if this.size == 0 {
				this.head = nil
				this.tail = nil
			}

			if this.inBegin {
				this.queue.Post(func() {
					this.callBegin(this.current)
				})
			} else {
				this.callBegin(this.current)
			}
		}
	}
}

func (this *TransactionMgr) Empty() bool {
	if nil != this.current {
		return false
	}

	if this.size > 0 {
		return false
	}

	return true
}
